import * as EventEmitter from 'events'
import {pick} from 'lodash'
import {DummyRecorder, IGameRecorder} from '../GameRecorder'
import {autoSerialize, autoSerializePropertyKeys, Serializable, serializeHelp} from "../serializeDecorator"
import Card from './card'
import Room from './room'
import Rule from './Rule'
import {default as Table, Team} from "./table"
import Timer = NodeJS.Timer


class DetailBalance {

  addScore(score: number, cate: string) {
    if (this[cate]) {
      this[cate] += score
    } else {
      this[cate] = score
    }
  }
}

export default class PlayerAgent implements Serializable {
  timeoutTask: Timer

  room: Room

  @autoSerialize
  zhuang: boolean = false

  @autoSerialize
  ip: string

  @autoSerialize
  cards: Card[]
  emitter: EventEmitter

  @autoSerialize
  events: any
  recorder: IGameRecorder
  record: (event: string, cards?: Card[]) => void
  rule: Rule
  model: any
  disconnectCallBack: (any) => void

  @autoSerialize
  score: number

  msgDispatcher: any
  onDeposit: boolean = false
  msgHook: any
  isOperated: boolean

  @autoSerialize
  winOrder = 99

  balance = 0
  detailBalance: DetailBalance = new DetailBalance()

  @autoSerialize
  unusedJokers: number = 0

  @autoSerialize
  index = 0

  @autoSerialize
  team = Team.NoTeam

  @autoSerialize
  teamMate: number = -1
  // foundTeamMate: boolean = false;

  @autoSerialize
  zhuaFen = 0

  @autoSerialize
  mode: "teamwork" | "solo" | "unknown" = 'unknown'

  @autoSerialize
  cleaned: boolean = false

  @autoSerialize
  foundFriend: boolean = false

  @autoSerialize
  isDiZhu: boolean = false

  @autoSerialize
  isMingPai: boolean = false

  @autoSerialize
  daCount: number = 0

  @autoSerialize
  myAnswerIndex = -1

  @autoSerialize
  myAnswerIsRight = false

  constructor(userSocket, room, rule) {
    this.room = room
    this.zhuang = false
    this.rule = rule
    this.ip = userSocket.getIpAddress()
    this.model = userSocket.model
    this.emitter = new EventEmitter()
    this.cards = []
    this.score = room.getScoreBy(userSocket)
    this.disconnectCallBack = player => {
      if (player === this.msgDispatcher) {
        this.onDisconnect()
      }
    }
    this.listenDispatcher(userSocket)
    this.msgDispatcher = userSocket
    this.events = {}

    this.isOperated = false
    this.recorder = new DummyRecorder()
    this.team = Team.AwayTeam
  }

  getCardsArray(): Card[] {
    return this.cards
  }


  listenDispatcher(socket) {

  }

  setGameRecorder(r) {
    this.recorder = r
    this.record = (event, cards?) => this.recorder.recordUserEvent(this, event, cards)
    return this
  }

  toJSON() {
    const playerStatJson = serializeHelp(this)
    playerStatJson._id = this.model._id
    return playerStatJson
  }

  resume(tableStateJson) {
    const keys = autoSerializePropertyKeys(this)
    Object.assign(this, pick(tableStateJson, keys))

    if (this.cards) {
      this.cards = this.cards.map(card => Card.from(card))
    }
  }

  get seatIndex() {
    return this.index
  }

  on(event, callback) {
    this.emitter.on(event, callback)
  }

  onDisconnect() {
    this.removeAllListeners()
  }

  removeAllListeners() {
    if (this.msgDispatcher) {
      const allNames = Object.keys(this.msgDispatcher.getGameMsgHandler())
      this.removeListenersByNames(allNames)
      this.msgDispatcher.removeListener('disconnect', this.disconnectCallBack)
    }
  }

  removeListenersByNames(names) {
    if (this.msgDispatcher) {
      names.forEach(name => this.msgDispatcher.removeAllListeners(name))
    }
  }

  sendMessage(name, data) {
    this.msgDispatcher.sendMessage(name, data)
    return data
  }

  reconnect(msgDispatcher) {
    this.msgDispatcher = msgDispatcher
    this.onDeposit = false
    this.clearDepositTask()
    this.listenDispatcher(msgDispatcher)
  }

  get _id(): string {
    return this.model._id
  }

  winFrom(loser: PlayerAgent, score: number, cate: string = 'base') {
    this.balance += score
    loser.balance -= score

    this.detailBalance.addScore(score, cate)
    loser.detailBalance.addScore(-score, cate)
  }

  private baseStatus(table: Table) {
    return {
      model: this.model,
      index: this.index,
      ip: this.ip,
      score: this.room.getScoreBy(this._id),
      remains: this.cards.length,
      mode: this.mode,
      team: this.team,
    }
  }

  statusForSelf(table: Table) {
    const base = this.baseStatus(table)
    return {
      ...base,
      cards: this.cards,
    }
  }

  statusForOther(table: Table) {
    return this.baseStatus(table)
  }

  clearDepositTask() {
    clearTimeout(this.timeoutTask)
  }


  cancelDeposit() {
    this.onDeposit = false
    const cards = this.cards
    this.clearDepositTask()
    this.sendMessage('game/cancelDeposit-ok', {cards})
  }


}
